package com.szamc.tableroundapi.service;

import com.szamc.tableroundapi.model.MultiTable.*;
import lombok.var;

import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.stream.Collectors;

public class MultiTableRoundApiService<T> {
    private static ArrayList<String> RowGroups;
    private static ArrayList<String> ColumnGroups;
    private static MultiTableOption MultiTableOptions;
    private static double DivideNumber;
    private static int Precision;
    public List<LinkedHashMap> GetRoundTable(List<LinkedHashMap> tableList, MultiTableOption _multiTableOptions,double _divideNumber,int _precision) throws NoSuchFieldException, IllegalAccessException {
        DivideNumber = _divideNumber;
        Precision = _precision;
        MultiTableOptions = _multiTableOptions;//全局
        RowGroups = GetRowGroups();//获取row分组，全局
        ColumnGroups = GetColumnGroups(tableList);//获取column分组，全局
        var multiTableFormatDataList = GetMultiTableFormatData(tableList);//获取按字段展开后的列表
        var groupRegionDataList =  GetGroupRegionDataList(multiTableFormatDataList);//获取按区域分分组
        var attachGroupRegionData = AttachRegionAddCount(groupRegionDataList,multiTableFormatDataList);
        var IdList = GetRegionAddIdList(attachGroupRegionData);
        var roundList = GetRoundList(tableList,IdList);
        var rowTotalRoundList = UpdateRowTotal(roundList);
        var columnTotalRoundList = UpdateColumnTotal(rowTotalRoundList);
        return columnTotalRoundList;
    }


    //获取行分组
    private ArrayList<String> GetRowGroups(){
        var rowGroups = new ArrayList<String>();
        var rowOptions = MultiTableOptions.rowOptions;
        for (int i = 0; i < rowOptions.size(); i++) {
            if(!rowGroups.contains(rowOptions.get(i).getGroupFieldName())){
                rowGroups.add(rowOptions.get(i).getGroupFieldName());
            }
        }
        return rowGroups;
    }

    //获取列分组
    private ArrayList<String> GetColumnGroups(List<LinkedHashMap> tableList) throws NoSuchFieldException, IllegalAccessException {
        var columnGroups = new ArrayList<String>();
        String columnGroupFieldName = MultiTableOptions.getColumnOption().groupFieldName;
        for(int i=0;i<tableList.size();i++){
            var obj = tableList.get(i);
            String columnValue = (String) obj.get(columnGroupFieldName);
            if(!columnGroups.contains(columnValue)){
                columnGroups.add(columnValue);
            }
        }
        return columnGroups;
    }

    //按字段展开
    public ArrayList<MultiTableFormatData> GetMultiTableFormatData(
            List<LinkedHashMap> tableList
    ) throws NoSuchFieldException, IllegalAccessException {
        var forMatGroupFormatDataList = new ArrayList<MultiTableFormatData>();
        for(int i=0;i<tableList.size();i++){
            for (RowOption multiRowOption : MultiTableOptions.rowOptions) {
                if (multiRowOption.getFieldType() != 0) {
                    LinkedHashMap obj = tableList.get(i);
                    var objValue = obj.get(multiRowOption.getFieldName());
                    var _value =  GetProcessValue(ObjToDouble(objValue));
                    var fieldType = multiRowOption.getFieldType();
                    var _groupColumnName = (String) obj.get(MultiTableOptions.getColumnOption().groupFieldName);
                    var _formatData = new MultiTableFormatData(
                            fieldType == 1 ? _value : -_value,
                            multiRowOption.getFieldName(),
                            multiRowOption.getFieldType(),
                            i,
                            multiRowOption.getGroupFieldName(),
                            _groupColumnName
                    );
                    forMatGroupFormatDataList.add(_formatData);
                }
            }
        }
        return forMatGroupFormatDataList;
    }

    //获取区域分组的数据
    private ArrayList<GroupRegionData>  GetGroupRegionDataList(ArrayList<MultiTableFormatData> multiTableFormatDataList){
        var groupRegionDataList = new ArrayList<GroupRegionData>();
        for(int i=0;i<RowGroups.size();i++){
            var groupDataList = new ArrayList<GroupData>();
            for(int j=0;j<ColumnGroups.size();j++){
                int finalI = i;
                int finalJ = j;
                var groupFormatData = multiTableFormatDataList
                        .stream()
                        .filter(s ->
                                s.getGroupRowName().equals(RowGroups.get(finalI))
                                        && s.getGroupColumnName().equals(ColumnGroups.get(finalJ)))
                        .collect(Collectors.toList());

                var groupData = GetGroupResult(
                        RowGroups.get(finalI),
                        ColumnGroups.get(finalJ),
                        GetSumValue(groupFormatData),
                        GetFloorSum(groupFormatData),
                        groupFormatData);
                groupDataList.add(groupData);
            }
            var groupRegionData = new GroupRegionData(RowGroups.get(i),groupDataList);
            groupRegionDataList.add(groupRegionData);
        }
        return GetCalculatedGroupRegionDataList(groupRegionDataList);
    }

    //计算区域分组的数据
    private ArrayList<GroupRegionData>  GetCalculatedGroupRegionDataList(ArrayList<GroupRegionData> groupRegionDataList){
        for(int i=0;i<groupRegionDataList.size();i++){
            var multiTableFormatDataList = new ArrayList<MultiTableFormatData>();
            for(int j=0;j<groupRegionDataList.get(i).getGroupDataList().size();j++){
                multiTableFormatDataList.addAll(groupRegionDataList.get(i).getGroupDataList().get(j).getMutiTableFormatData());
            }
            var _sum = GetSumValue(multiTableFormatDataList);
            var _floorSum = GetFloorSum(multiTableFormatDataList);
            var _addcount =(Math.round(_sum))-_floorSum;
            groupRegionDataList.get(i).setSum(_sum);
            groupRegionDataList.get(i).setAddCount((int) _addcount);
            groupRegionDataList.get(i).setFloorsum(_floorSum);
        }
        return groupRegionDataList;
    }

    private GroupData GetGroupResult (String groupName,String columnName,double sumValue,int sumFloor,List<MultiTableFormatData> tempResult){
        return new GroupData(
                groupName + "_" + columnName,
                groupName,
                columnName,
                sumValue,
                sumFloor,
                (int) Math.floor(sumValue - sumFloor),//add count
                sumValue-Math.floor(sumValue),
                tempResult
        );
    }
    private double GetProcessValue(double OriginalValue){
        return  (OriginalValue/DivideNumber)*Math.pow(10, Precision);
    }

    private double GetOriginalValue(double ProcessValue,int fieldType){
        double result = round(ProcessValue/Math.pow(10, Precision), Precision);
        return fieldType==1?result:-result;
    }
    private static double round(double value, int places) {
        BigDecimal bd = BigDecimal.valueOf(value);
        bd = bd.setScale(places, RoundingMode.DOWN);
        return bd.doubleValue();
    }

    private double GetSumValue(List<MultiTableFormatData> tempResult){
        double sum = 0;
        for(int i=0;i<tempResult.size();i++){
            sum = sum +tempResult.get(i).getValue();
        }
        return sum;
    }

    private int GetFloorSum(List<MultiTableFormatData> tempResult){
        int sum = 0;
        for(int i=0;i<tempResult.size();i++){
            sum = sum+ (int) Math.floor(tempResult.get(i).getValue());
        }
        return sum;
    }

    private double GetSumValue(ArrayList<MultiTableFormatData> tableRows){
        double sum = 0;
        for(int i=0;i<tableRows.size();i++){
            sum = sum +tableRows.get(i).getValue();
        }
        return sum;
    }

    private int GetFloorSum(ArrayList<MultiTableFormatData> tableRows){
        int sum = 0;
        for(int i=0;i<tableRows.size();i++){
            sum = sum+ (int) Math.floor(tableRows.get(i).getValue());
        }
        return sum;
    }

    private int GetGroupAllAddCount(List<GroupData> GroupDataList){
        int sum = 0;
        for(int i=0;i<GroupDataList.size();i++){
            sum = sum + GroupDataList.get(i).getAddCount();
        }
        return sum;
    }
    //分区域（Region）分配加1指标
    private ArrayList<GroupRegionData> AttachRegionAddCount(ArrayList<GroupRegionData> groupRegionDataList,ArrayList<MultiTableFormatData> multiTableFormatDataList){
        for(int i=0;i<groupRegionDataList.size();i++){
            var _groupDataList = groupRegionDataList.get(i).getGroupDataList();
            var _groupName = groupRegionDataList.get(i).getGroupName();
            var _multiTableFormatDataList = multiTableFormatDataList.stream().filter(p -> p.getGroupRowName().equals(_groupName))
                    .collect(Collectors.toCollection(ArrayList::new));
            var _result = AttachAddCount(_groupDataList,_multiTableFormatDataList);
            groupRegionDataList.get(i).setGroupDataList(_result);
        }
        return groupRegionDataList;
    }
    //给“分组”分配加1指标
    private ArrayList<GroupData> AttachAddCount(ArrayList<GroupData> groupDataList,ArrayList<MultiTableFormatData> multiTableFormatDataList){
        //计算出总的还有多少指标
        var _SumValue = GetSumValue(multiTableFormatDataList);
        var _FloorSum = GetFloorSum(multiTableFormatDataList);
        var remainAddCount = (int)Math.round(_SumValue-_FloorSum-GetGroupAllAddCount(groupDataList));
        groupDataList.sort(Comparator.comparing(GroupData::getDecimalPart).reversed());//排序
        for(var i=0;i<remainAddCount;i++){
            var _addCount = groupDataList.get(i).getAddCount();
            groupDataList.get(i).setAddCount(_addCount + 1);
        }
        return groupDataList;
    }

    private  List<String> GetRegionAddIdList (ArrayList<GroupRegionData> attachGroupRegionData){
        var ids = new ArrayList<String>();
        for(int i=0;i<attachGroupRegionData.size();i++){
           var _groupDataList = attachGroupRegionData.get(i).getGroupDataList();
           var _ids = GetAddIdList(_groupDataList);
            ids.addAll(_ids);
        }
        return ids;
    }

    private  List<String> GetAddIdList (ArrayList<GroupData> groupDataList){
        var ids = new ArrayList<String>();
        for(int i=0;i<groupDataList.size();i++){
            groupDataList.get(i).getMutiTableFormatData().sort(Comparator.comparing(MultiTableFormatData::getDecimalPart).reversed());//排序
            for(var j=0;j<groupDataList.get(i).getAddCount();j++){
                ids.add(groupDataList.get(i).getMutiTableFormatData().get(j).getId());
            }
        }
        return ids;
    }

    private String GetTableId(int rowIndex,String fieldName){
        return rowIndex + "_" + fieldName;
    }

    private  double ObjToDouble(Object objValue){
        var _value =0.00;
        if(objValue instanceof Integer){
            _value = ((Integer) objValue).doubleValue();
        }
        else{
            _value = (Double) objValue;
        }
        return _value;
    }

    //给原来的List按条件+1
    private List<LinkedHashMap> GetRoundList(List<LinkedHashMap> tableList, List<String> Ids) throws NoSuchFieldException, IllegalAccessException {
        for(int i=0;i<tableList.size();i++){
            for ( RowOption item : MultiTableOptions.rowOptions) {
                var _id = GetTableId(i,item.getFieldName());
                LinkedHashMap obj = tableList.get(i);
                int fieldType = item.getFieldType();
                var _oldValue =  ObjToDouble(obj.get(item.getFieldName()));
                var _newValue = Math.floor(fieldType== 1 ? GetProcessValue(_oldValue):-GetProcessValue(_oldValue));
                var addNumber = 1;
                if (Ids.contains(_id)) {
                    obj.replace(item.getFieldName(), GetOriginalValue(_newValue + addNumber,fieldType));
                } else {
                    obj.replace(item.getFieldName(), GetOriginalValue(_newValue,fieldType));
                }
            }
        }
        return tableList;
    }
    private double sum(double d1,double d2){
        BigDecimal bd1 = BigDecimal.valueOf(d1);
        BigDecimal bd2 = BigDecimal.valueOf(d2);
        return bd1.add(bd2).doubleValue();
    }


    //更新Row合计
    private List<LinkedHashMap> UpdateRowTotal(List<LinkedHashMap> tableList) throws NoSuchFieldException, IllegalAccessException {
        for(int i=0;i<tableList.size();i++){
            LinkedHashMap obj = tableList.get(i);
            for(int j=0;j<RowGroups.size();j++){//分组求合计
                int finalJ = j;
                List<RowOption> groupRowOptions =
                        MultiTableOptions.rowOptions.stream()
                        .filter(d -> d.getGroupFieldName().equals(RowGroups.get(finalJ)))
                        .filter(d -> d.getIsTotalField()==0)
                        .collect(Collectors.toList());//找出一个分组中的普通字段
                double sum = 0.00;
                for(int k = 0;k<groupRowOptions.size();k++){
                    var objValue =(double) obj.get(groupRowOptions.get(k).getFieldName());
                    sum = sum(sum,objValue);
                }
                var totalOption =
                        MultiTableOptions.rowOptions.stream()
                                .filter(d -> d.getGroupFieldName().equals(RowGroups.get(finalJ)))
                                .filter(d -> d.getIsTotalField()==1)
                                .collect(Collectors.toList()).get(0);//一个分组中只能有一个合计字段
                obj.replace(totalOption.getFieldName(), sum);

            }
        }
        return tableList;
    }

    //更新Column合计
    private List<LinkedHashMap> UpdateColumnTotal(List<LinkedHashMap> tableList) throws NoSuchFieldException, IllegalAccessException {
            List<RowOption> groupRowOptions =MultiTableOptions.rowOptions;
            String totalRecordValue = MultiTableOptions.columnOption.totalRecordValue;
            for(int i=0;i<groupRowOptions.size();i++){
                double sum = 0.00;
                int totalObjIndex = 0;
                for(int j = 0;j<tableList.size(); j++){
                    LinkedHashMap obj = tableList.get(j);
                    var fieleValue = obj.get(MultiTableOptions.getColumnOption().totalRecordFieldName);
                    if(!fieleValue.toString().equals(totalRecordValue)){
                        var objValue =(double) obj.get(groupRowOptions.get(i).getFieldName());
                        sum = sum(sum,objValue);
                    }
                    else{
                        totalObjIndex = j;
                    }
                }
                tableList.get(totalObjIndex).replace(groupRowOptions.get(i).getFieldName(), sum);
            }

        return tableList;
    }

}
